﻿using System;
using System.Globalization;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.IO;
using System.Windows.Forms;
namespace System
{
	public partial struct V3G
	{
		public const double EarthRadius = 6371008.7714;

		double _longitude, _latitude;
		public int LongDeg, LongMin, LongSec, LongFr;
		public int LatDeg, LatMin, LatSec, LatFr;
		public bool LongN, LatN;

		public double Longitude
		{
			get
			{
				return _longitude;
			}
			set
			{
				//if (value < -180)
				//	Longitude = value + 360;
				//else if (value > 180)
				//	Longitude = value - 360;
				//else
				{
					_longitude = value;
					LongD = (int)Math.Round(_longitude * 3600000);
					if (!value.Equals(double.NaN))
					{
						LongN = value < 0;
						int P = Math.Abs(LongD);
						LongFr = P % 1000;
						P /= 1000;
						LongSec = P % 60;
						P /= 60;
						LongMin = P % 60;
						P /= 60;
						LongDeg = P;
					}
				}
			}
		}
		public double Latitude
		{
			get
			{
				return _latitude;
			}
			set
			{
				if (value > 90)
				{
					Latitude = 90;
				}
				else if (value < -90)
				{
					Latitude = -90;
				}
				else
				{
					_latitude = value;
					LatD = (int)Math.Round(_latitude * 3600000);
					if (!value.Equals(double.NaN))
					{
						LatN = value < 0;
						int P = Math.Abs(LatD);
						LatFr = P % 1000;
						P /= 1000;
						LatSec = P % 60;
						P /= 60;
						LatMin = P % 60;
						P /= 60;
						LatDeg = P;
					}
				}
			}
		}

		int LongD, LatD;
		public String LongS
		{
			get
			{
				return LongDeg.ToString("000") + "°" + LongMin.ToString("00") + "'" + LongSec.ToString("00") + "." + LongFr.ToString("000") + "\"" + (LongN ? "W" : "E");
			}
		}
		public String LatS
		{
			get
			{
				return LatDeg.ToString("000") + "°" + LatMin.ToString("00") + "'" + LatSec.ToString("00") + "." + LatFr.ToString("000") + "\"" + (LatN ? "S" : "N");
			}
		}

		public double X
		{
			get
			{
				return (V3G.EarthRadius + _Height) * Math.Sin(Longitude / 180 * Math.PI) * Math.Cos(Latitude / 180 * Math.PI);
			}
		}
		public double Y
		{
			get
			{
				return (V3G.EarthRadius + _Height) * Math.Sin(Latitude / 180 * Math.PI);
			}
		}
		public double Z
		{
			get
			{
				return (V3G.EarthRadius + _Height) * Math.Cos(Longitude / 180 * Math.PI) * Math.Cos(Latitude / 180 * Math.PI);
			}
		}

		public double _Height;
		public V3G NewHeight(double h)
		{
			return new V3G(Longitude, Latitude, h);
		}
		public double HeightFeet
		{
			get
			{
				return Dimensions.Length(_Height.ToString("0.000") + "m", "ft");
			}
			set
			{
				_Height = Dimensions.Length(value + "ft", "m");
			}
		}
		public int HeightMSL
		{
			get
			{
				return (int)(Math.Round(HeightFeet / 100));
			}
		}
		public bool Valid;

		#region Constructors
		public V3G(double longit, double latit)
			: this(longit, latit, 0)
		{
		}
		public V3G(double longit, double latit, double hei)
		{
			LongN = LatN = false;
			LongD = LongDeg = LongMin = LongSec = LongFr = 0;
			LatD = LatDeg = LatMin = LatSec = LatFr = 0;
			_longitude = _latitude = 0;
			_Height = hei;
			Valid = false;
			Longitude = longit;
			Latitude = latit;
			Valid = !(longit.Equals(double.NaN) || latit.Equals(double.NaN));
			//Index1 = Index2 = 0;
		}

		const String LatitExp = "[0-9]+(|\\.?[0-9]+)[NS]";
		const String LongitExp = "[0-9]+(|\\.?[0-9]+)[EW]";
		public const String SearchExp = LatitExp + " *?" + LongitExp;
		public V3G(String S)
			: this(0, 0)
		{
			try
			{
				String Lat = Regex.Match(S, LatitExp).Captures[0].Value;
				if (Lat.IndexOf('.') == 6 || (Lat.IndexOf('.') == -1 && Lat.Length == 7))
					Latitude = int.Parse(Lat.Substring(0, 2)) + int.Parse(Lat.Substring(2, 2)) / 60.0f + double.Parse(Lat.Substring(4, Lat.Length - 5), CultureInfo.InvariantCulture) / 3600.0 + 1e-12;
				else
					Latitude = double.Parse(Lat.Substring(0, Lat.Length - 1), CultureInfo.InvariantCulture);
				if (Lat.Contains("S"))
					Latitude *= -1;

				String Long = Regex.Match(S, LongitExp).Captures[0].Value;
				if (Long.IndexOf('.') == 7 || (Long.IndexOf('.') == -1 && Long.Length == 8))
					Longitude = int.Parse(Long.Substring(0, 3)) + int.Parse(Long.Substring(3, 2)) / 60.0f + double.Parse(Long.Substring(5, Long.Length - 6), CultureInfo.InvariantCulture) / 3600.0 + 1e-12;
				else
					Longitude = double.Parse(Long.Substring(0, Long.Length - 1), CultureInfo.InvariantCulture);
				if (Long[Long.Length - 1] == 'W')
					Longitude = -Longitude;
				Valid = true;
			}
			catch
			{
				Longitude = Latitude = 0;
				Valid = false;
				throw new Exception("Wrong Format");
			}
		}
		#endregion

		public System.Drawing.PointF ToPointF()
		{
			return new System.Drawing.PointF((float)Longitude, (float)Latitude);
		}
		public static int CompLongLat(V3G P1, V3G P2)
		{
			if (P1.Longitude < P2.Longitude)
				return -1;
			if (P1.Longitude > P2.Longitude)
				return 1;
			if (P1.Latitude < P2.Latitude)
				return -1;
			if (P1.Latitude > P2.Latitude)
				return 1;
			return 0;
		}
		public static int CompIndex(V3G P1, V3G P2)
		{/*
			if (P1.Index2 < P2.Index2)
				return -1;
			if (P1.Index2 > P2.Index2)
				return 1;
			if (P1.Index1 < P2.Index1)
				return -1;
			if (P1.Index1 > P2.Index1)
				return 1;
		*/
			return 0;
		}
		public override String ToString()
		{
			if (Valid)
			{
				String S = "";
				S += String.Format("{0:00}{1:00}{2:00}.{3:000}", LatDeg, LatMin, LatSec, LatFr);
				S += Latitude < 0 ? "S" : "N";
				S += String.Format(" {0:000}{1:00}{2:00}.{3:000}", LongDeg, LongMin, LongSec, LongFr);
				S += Longitude < 0 ? "W" : "E";
				//S += " H:" + _Height;
				return S;
			}
			else
				return "<<<Invalid>>>";
		}
		public static V3G Null
		{
			get
			{
				V3G Res = new V3G(double.NaN, double.NaN, 0);
				Res.Valid = false;
				return Res;
			}
		}
		public static V3G North
		{
			get
			{
				V3G Res = new V3G(0, 90, 0);
				Res.Valid = true;
				return Res;
			}
		}
		public override int GetHashCode()
		{
			return (int)(IntPtr)Longitude;
		}
		public override bool Equals(object obj)
		{
			V3G X = (V3G)obj;
			return Valid && X.Valid && Longitude == X.Longitude && Latitude == X.Latitude;
		}
		public static bool operator ==(V3G G1, V3G G2)
		{
			if (!(G1.Valid && G2.Valid))
				return false;
			else
				if (G1.Latitude == 90 && G2.Latitude == 90)
					return true;
				else if (G1.Latitude == -90 && G2.Latitude == -90)
					return true;
				else
					return G1.Longitude == G2.Longitude && G1.Latitude == G2.Latitude && G1._Height == G2._Height;
		}
		public static bool operator !=(V3G G1, V3G G2)
		{
			if (!(G1.Valid && G2.Valid))
				return true;
			else
				return !(G1.Longitude == G2.Longitude && G1.Latitude == G2.Latitude && G1._Height == G2._Height);
		}
		public static V3G operator +(V3G G, V3D V)
		{
			if (V.Length == 0 || !G.Valid)
				return G;
			else
			{
				V3D Res = (V3D)G;
				V3D N = (Res % (Res + V.Unit)).Unit;
				V3D Vert = V.ParallelPart(Res);
				V3D Horiz = V.CrossPart(Res);
				double a = Horiz.Length / ((V3G.EarthRadius + G._Height) * 2 * Math.PI) * 360;
				if (a.Equals(double.NaN))
					a = 0;
				return (Res + Vert).Rot(N, a);
			}
		}

		public V3D Project(V3D V)
		{
			return V.RotX(-Latitude).RotY(Longitude);
		}
		public V3D UnProject(V3D V)
		{
			return V.RotY(-Longitude).RotX(Latitude);
		}

		public V3D GetDirection(V3G P)
		{
			V3D P0 = (V3D)this;
			V3D P1 = (V3D)P;

			V3D Res = (P1 - P0).LengthSq == 0 ? V3D.Black : (P1 - P0).CrossPart(P0);
			return Res;
			//V3D N = (P0 % P1).Unit;
			//return (N % P0).Unit;
		}
		public V3D GetLocalDirection(V3G P)
		{
V3D res=UnProject(GetDirection(P));
			res.Z = _Height - P._Height;
			return res;
		}
		public double GetHeading(V3G Position)
		{
			V3D NorthDir = ((V3D)North - (V3D)this).CrossPart(this).Unit;
			V3D V = ((V3D)Position - (V3D)this).CrossPart(this).Unit;
			return V3D.Angle360D(NorthDir, V, this);
		}
		public double GetDist(V3G P)
		{
			return V3D.Angle(this, P) * EarthRadius;
			//return /*GetHeading(P) */ (this - P).Length;
		}

		/// <summary>Moves object around 3G space, returns new heading
		/// <param name="NewHeading">Returns new heading to maintain direction</param>
		/// </summary>
		public void Move(double Heading, double Dist, out double NewHeading)
		{
			V3D NorthDir = ((V3D)North - (V3D)this).CrossPart(this).Unit;
			V3D Vel = NorthDir.Rot(this, -Heading);
			V3G NewP2 = this + Vel * Dist;
			V3G NewP = this.AddLocal(Dist, Heading);
			V3D N = Vel.Rot(this, 90).Unit;
			Vel = Vel.Rot(N, Dist / ((V3D)this).Length/ Math.PI * 180);
			this = NewP;
			NorthDir = ((V3D)North - (V3D)this).CrossPart(this).Unit;
			Heading = V3D.Angle360D(NorthDir, Vel, this);
			if (Heading > 360)
				Heading -= 360;
			if (Heading < 0)
				Heading += 360;
			NewHeading = Heading;
		}
		public void Move(V3D Dir2D, out double NewHead)
		{
			Move(V3D.Angle360D(V3D.Green, Dir2D, V3D.Blue), Dir2D.Length, out NewHead);
		}

		public V3G AddLocal(V3D L)
		{
			V3D arc = Project(L);
			V3D N = ((V3D)this) % arc;
			return ((V3D)this).Rot(N, L.Length / (EarthRadius * Math.PI) * 180);
			//return this + Project(L);
		}
		//Summary: Adds local vector using heading and distance;returns new point
		public V3G AddLocal(double l, double h)
		{
			return AddLocal(V3D.Green.Rot(V3D.Blue, -h) * l);
		}

		public static implicit operator V3D(V3G V)
		{
			return new V3D(V.X, V.Y, V.Z);
			//return (V3D.Blue * (V.Radius + V.Height)).Rot(-V3D.Red, V.Latitude).Rot(V3D.Green, V.Longitude);
		}
		public static implicit operator V3G(V3D V)
		{
			if (V.Valid)
			{
				V3G Res = V3G.Null;
				try
				{
					Res._Height = V.Length - V3G.EarthRadius;
					Res.Latitude = Math.Asin(V.Y / V.Length) / Math.PI * 180;
					Res.Longitude = Math.Acos(V.Z / Math.Sqrt(V.X * V.X + V.Z * V.Z)) / Math.PI * 180 * (V.X < 0 ? -1 : 1);
					Res.Valid = true;
				}
				catch
				{
					Res = V3G.Null;
				}
				return Res;
			}
			else
				return V3G.Null;
		}

		public static V3D operator -(V3G G1, V3G G2)
		{
			V3D N = ((V3D)G1).Unit;
			V3D D = new Line3G(G1, G2, false).Dist;
			if (D.Length == 0)
				return D;
			else
				return ((D % N) % N).Unit * D.Length;
		}

		public bool InRect(V3G P1, V3G P2)
		{
			return new V3GAABB(P1, P2).PointIn(this);
			//return Longitude >= P1.Longitude && Longitude <= P2.Longitude && Latitude >= P1.Latitude && Latitude <= P2.Latitude;
		}

		public static bool RectIntersect(V3G P1, V3G P2, V3G P3, V3G P4)
		{
			if (P1.Longitude > P4.Longitude)
				return false;
			else if (P2.Longitude < P3.Longitude)
				return false;
			else if (P1.Latitude > P4.Latitude)
				return false;
			else if (P2.Latitude < P3.Latitude)
				return false;
			else
				return true;
		}
		public bool InV3GPolygon(V3G[] Polygon, V3G Pivot)
		{
			V3G Min = V3G.Null;
			V3G Max = V3G.Null;
			Min = Max = Polygon[0];
			for (int i = 1; i < Polygon.Length; i++)
			{
				Min.Longitude = Math.Min(Min.Longitude, Polygon[i].Longitude);
				Min.Latitude = Math.Min(Min.Latitude, Polygon[i].Latitude);
				Max.Longitude = Math.Max(Max.Longitude, Polygon[i].Longitude);
				Max.Latitude = Math.Max(Max.Latitude, Polygon[i].Latitude);
			}

			int N = 0;
			if (InRect(Min, Max))
			{
				for (int i = 0; i < Polygon.Length; i++)
				{
					V3D L1 = Polygon[i];
					V3D L2 = Polygon[(i + 1) % Polygon.Length];
					bool b1 = (((V3D)this % L1) % (L1 % (V3D)Pivot)).Length == 0;
					bool b2 = (((V3D)this % L2) % (L2 % (V3D)Pivot)).Length == 0;
					if (b1 && b2)
					{
					}
					else if (b1 || b2)
					{
						if (b1)
							N++;
					}
					else
					{
						V3G M = new Line3G(Pivot, this, false) * new Line3G(L1, L2, false);
						if (M.Valid)
							N++;
						else
						{
						}
					}
				}
			}
			bool Test1 = N % 2 == 1;
			return Test1;
		}
		public static V3G Interpolate(V3G V1, V3G V2, double n)
		{
			double angle = V3D.AngleD(V1, V2);
			V3D N = ((V3D)V1 % (V3D)V2).Unit;
			return (V3G)(((V3D)V1).Rot(N, angle * n));
		}
		public static V3G CircleCenter(V3G P1, V3G P2, V3G P3)
		{
			V3D A = P1;
			V3D B = P2;
			V3D C = P3;
			V3D N = (P2 - P1) % (P3 - P1).Unit;
			/*
			Line3G L1 = new Line3G(V3G.Interpolate(P1, P2, 0.5), V3G.Null, false);
			V3D D1 = L1.A.GetDirection(P2).Rot(L1.A, 90);
			L1.B = L1.A + D1;
			Line3D B1 = new Line3D(L1.A, L1.B);

			Line3G L2 = new Line3G(V3G.Interpolate(P2, P3, 0.5), V3G.Null, false);
			V3D D2 = L2.A.GetDirection(P3).Rot(L2.A, 90);
			L2.B = L2.A + D2;
			Line3D B2 = new Line3D(L2.A, L2.B);

			V3D Center1 = B1.DistanceLine(B2);
			V3D Center2 = B2.DistanceLine(B1);
			V3G Res = (Center1 + Center2) / 2;
			Res._Height = 0;
			return Res;
			*/
			V3D A1 = (A + B) / 2;
			V3D A2 = A1 + (B - A).Unit.Rot(A1, -90);
			V3D B1 = (B + C) / 2;
			V3D B2 = B1 + (C - B).Unit.Rot(B1, -90);
			V3G Res = new Line3D(A1, A2) * new Line3D(B1, B2);
			return Res;
		}
		public static V3G[] GetArc(V3G P1, V3G P2, V3G P3, int leavinglasts)
		{
			List<V3G> Res = new List<V3G>();

			V3D C = V3D.CircleCenter(P1, P2, P3);
			V3D D1 = C - (V3D)P1;
			V3D D2 = C - (V3D)P2;
			V3D D3 = C - (V3D)P3;

			double Start = ((V3G)C).GetHeading(P1);
			double End = Start - (V3D.Angle360(D1, D3, C) > V3D.Angle360(D1, D2, C) ? V3D.Angle360D(D1, D3, C) : V3D.Angle360D(D1, D3, C) - 360);
			int R = (int)(Math.Abs(End - Start) / 5);
			for (int i = 0; i <= R; i++)
			{
				V3G NP = ((V3D)C) + ((V3D)P1 - (V3D)C).Rot(C, (End - Start) * i / R);
				Res.Add(NP);
			}
			V3G[] res = new V3G[Res.Count - leavinglasts];
			Res.CopyTo(0, res, 0, Res.Count - leavinglasts);
			return res;
		}

		public static Circle3G GetMEC(List<V3G> Points)
		{
			return new Circle3G(V3G.Null, -1);
		}

		public V3G ReMap(V3G A, V3G B, V3G C, V3G D)
		{
			V3D R1 = V3D.Red.RotY(A.Longitude);
			V3D R2 = V3D.Red.RotY(B.Longitude);
			V3D P = V3D.Red.RotY(_longitude);
			V3D N1 = V3D.Red.RotY(C.Longitude);
			V3D N2 = V3D.Red.RotY(D.Longitude);

			double A1 = V3D.AbsAngleD(R1, P, V3D.Green);
			double A2 = V3D.AbsAngleD(R1, R2, V3D.Green);

			double N = V3D.AbsAngleD(N1, N2, V3D.Green);

			V3D ResLongit = C;
			if (A2 != 0)
				ResLongit = ResLongit.RotY(Algebra3D.Map(0, A2, A1, 0, N));
			double reslongit = ((V3G)ResLongit).Longitude;
			double reslatit;
			if (C.Latitude == D.Latitude || A.Latitude == B.Latitude)
				reslatit = C.Latitude;
			else
				reslatit = Algebra3D.Map(A.Latitude, B.Latitude, _latitude, C.Latitude, D.Latitude);
			return new V3G(reslongit, reslatit);
		}

		public void Append(List<byte> Buf)
		{
			Buf.AddRange(BitConverter.GetBytes(Longitude));
			Buf.AddRange(BitConverter.GetBytes(Latitude));
		}
		public V3G(byte[] msg, ref int msgptr)
			: this(0,0)
		{
			Longitude = BitConverter.ToDouble(msg, msgptr);
			msgptr += 8;
			Latitude = BitConverter.ToDouble(msg, msgptr);
			msgptr += 8;
			_Height = 0;
		}

		public static void GetCoord(Control C)
		{
			V3G V = V3G.Null;
			new fDialogCoords(C.Text).SetCoord(ref V);
			if (V.Valid)
				C.Text = V.ToString();
		}

	}
	public class V3GObj
	{
		public static List<List<V3G>> Results = new List<List<V3G>>();
		static List<List<V3G>> Stack = new List<List<V3G>>();
		//static List<bool>
		
		public static void _SearchCircles()
		{
		}
		public static List<List<V3G>> SearchCircles(List<List<V3G>> Points)
		{
			return null;
			/*Stack.Clear();
			Stack.AddRange(Points.ToArray());

			while (Stack.Count != 0)
			{
				List<int> Res = new List<int>();
				List<bool> Dir = new List<bool>();

			}*/
		}
	}
	public struct V3GAABB
	{
		public V3G Min, Max;
		public bool valid;
		public V3GAABB(V3G min, V3G max)
		{
			Min = min;
			Max = max;
			valid = true;
		}
		public bool PointIn(V3G Point)
		{
			return Point.Latitude >= Min.Latitude && Point.Latitude <= Max.Latitude
			&& (V3D.Red.RotY(Point.Longitude) % V3D.Red.RotY(Min.Longitude)).Y <= 0
			&& (V3D.Red.RotY(Point.Longitude) % V3D.Red.RotY(Max.Longitude)).Y >= 0;
		}
		public static V3GAABB operator *(V3GAABB A1, V3GAABB A2)
		{
			if (A1.valid && A2.valid &&
				A1.Min.Latitude <= A2.Max.Latitude && A1.Max.Latitude >= A2.Min.Latitude
						   && (V3D.Red.RotY(A1.Min.Longitude) % V3D.Red.RotY(A2.Max.Longitude)).Y >= 0
						   && (V3D.Red.RotY(A1.Max.Longitude) % V3D.Red.RotY(A2.Min.Longitude)).Y <= 0)
			{
				V3GAABB Res;
				Res.Min = Res.Max = new V3G(0, 0);
				Res.valid = true;

				Res.Min.Latitude = (A1.Min.Latitude > A2.Min.Latitude ? A1 : A2).Min.Latitude;
				Res.Max.Latitude = (A1.Max.Latitude < A2.Max.Latitude ? A1 : A2).Max.Latitude;
				Res.Min.Longitude = ((V3D.Red.RotY(A1.Min.Longitude) % V3D.Red.RotY(A2.Min.Longitude)).Y <= 0 ? A1 : A2).Min.Longitude;
				Res.Max.Longitude = ((V3D.Red.RotY(A1.Max.Longitude) % V3D.Red.RotY(A2.Max.Longitude)).Y >= 0 ? A1 : A2).Max.Longitude;
				return Res;
			}
			else
			{
				V3GAABB Res = new V3GAABB(V3G.Null, V3G.Null);
				Res.valid = false;
				return Res;
			}
		}
	}
	public enum Poly2GFormat
	{
		Decimal,
		NS_Coord
	}
	public class Poly2G
	{
		public List<V3G> Points;
		public V3G AABBMin, AABBMax;

		public Poly2G(V3G[] Pts)
		{
			Points = new List<V3G>(Pts);
			BuildAABB();
		}
		public Poly2G(String FileName, Poly2GFormat Format)
		{
			if (Format == Poly2GFormat.Decimal)
			{
				String[] Pts = File.ReadAllText(FileName).Split(' ');
				V3G[] pts = new V3G[Pts.Length];
				for (int i = 0; i < Pts.Length; i++)
					pts[i] = new V3G(float.Parse(Pts[i].Split(',')[0]), float.Parse(Pts[i].Split(',')[1]));
				Points = new List<V3G>(pts);
			}
			else if (Format == Poly2GFormat.NS_Coord)
			{
				String[] Pts = File.ReadAllLines(FileName);
				V3G[] pts = new V3G[Pts.Length];
				for (int i = 0; i < Pts.Length; i++)
					pts[i] = new V3G(Pts[i]);
				Points = new List<V3G>(pts);
			}
			BuildAABB();
		}
		public void BuildAABB()
		{
			AABBMin = AABBMax = Points[0];
			for (int i = 1; i < Points.Count; i++)
			{
				V3D P1 = V3D.Red.RotY(AABBMin.Longitude);
				V3D P2 = V3D.Red.RotY(AABBMax.Longitude);
				V3D P3 = V3D.Red.RotY(Points[i].Longitude);

				if ((P1 % P3).Y < 0)
					AABBMin.Longitude = Points[i].Longitude;
				AABBMin.Latitude = Math.Min(AABBMin.Latitude, Points[i].Latitude);

				if ((P2 % P3).Y > 0)
					AABBMax.Longitude = Points[i].Longitude;
				AABBMax.Latitude = Math.Max(AABBMax.Latitude, Points[i].Latitude);
			}
		}

		public bool Quicktest(V3G P)
		{
			return P.Longitude >= AABBMin.Longitude && P.Longitude <= AABBMax.Longitude && P.Latitude >= AABBMin.Latitude && P.Latitude <= AABBMax.Latitude;
		}
		public bool Quicktest(Line3G L)
		{
			V3G P2 = new V3G(AABBMax.Longitude, AABBMin.Latitude);
			V3G P4 = new V3G(AABBMin.Longitude, AABBMax.Latitude);
			V3G[] poly = new V3G[] { AABBMin, P2, AABBMax, P4, AABBMin };
			double D;
			V3G M = new Poly2G(poly).IntersectLine(L, out D);
			return D != -1;
		}

		public bool PointInside(V3G P, V3G Out)
		{
			if (!Quicktest(P))
				return false;
			int N = 0;
			int NP = Points.Count - 1;
			for (int i = 0; i < NP; i++)
			{
				V3G M = new Line3G(P, Out, false) * new Line3G(Points[i], Points[i + 1], false);
				if (M.Valid)
					N++;
			}
			return N % 2 == 1;
		}
		public V3G IntersectLine(Line3G P, out double Dist)
		{
			if (Points.Count < 8 || Quicktest(P))
			{
				List<V3G> M = new List<V3G>();
				int NP = Points.Count - 1;
				for (int i = 0; i < NP; i++)
				{
					V3G m = P * new Line3G(Points[i], Points[i + 1], false);
					if (m.Valid)
						M.Add(m);
				}
				if (M.Count == 0)
				{
					Dist = -1;
					return V3G.Null;
				}
				Dist = new Line3G(P.A, M[0], false).Length;
				V3G Res = M[0];
				for (int i = 1; i < M.Count; i++)
				{
					double d = new Line3G(P.A, M[i], false).Length;
					if (d < Dist)
					{
						Res = M[i];
						Dist = d;
					}
				}
				return Res;
			}
			Dist = -1;
			return V3G.Null;
		}

		public static bool AABBIntersect(Poly2G P1, Poly2G P2)
		{
			if (P1.AABBMax.Latitude < P2.AABBMin.Longitude || P1.AABBMin.Latitude > P2.AABBMax.Latitude)
				return false;
			if ((V3D.Red.RotY(P1.AABBMax.Longitude) % V3D.Red.RotY(P2.AABBMin.Longitude)).Y > 0
				|| (V3D.Red.RotY(P1.AABBMin.Longitude) % V3D.Red.RotY(P2.AABBMax.Longitude)).Y < 0)
				return false;
			return true;
		}

	}
}